#include "app/app_serial.h"
#include "thirdparty/log.h"

/// @brief 这是把串口设备初始化
/// @param serial_device 串口设备
/// @param filename 串口设备名字
/// @return 成功 0 失败返回 -1
int app_serial_init(SerialDevice *serial_device,char * filename)
{
    //初始化通用设备
    if (app_device_init(&serial_device->super, filename) < 0)
    {
        log_warn("app_device_init error");
        return -1;
    }
    //设置波特率
    if(app_serial_setBaud(serial_device,baud_9600) < 0)
    {
        log_warn("aapp_serial_init set buan fail");
        //失败
        return -1;
    }
    //设置数据为
    if(app_serial_setDataDits(serial_device,DataDits_8) < 0)
    {
        log_warn("aapp_serial_init set data_dist fail");
        //失败
        return -1;
    }
    //设置停止位
    if(app_serial_setStopDits(serial_device,StopDits_1) < 0)
    {
        log_warn("aapp_serial_init set stop_dist fail");
        //失败
        return -1;
    }
    //设置校验位
    if(app_serial_setParity(serial_device,Parity_None) < 0)
    {
        log_warn("aapp_serial_init set parity fail");
        //失败
        return -1;
    }
    //设置非阻塞模式
    if(app_serial_setBlockMote(serial_device,0) < 0)
    {
        log_warn("aapp_serial_init set not block fail");
        return -1;
    }
    //设置纯数据模式
    if(app_serial_setRawMode(serial_device) < 0)
    {
        log_warn("aapp_serial_init set raw mode fail");
        return -1;
    }
    //把配置耍姓进取
    if(app_serial_flush(serial_device) < 0)
    {
        log_warn("aapp_serial_init flush fail");
        return -1;
    }
    log_trace("app_serial_init succeed");
    return 0;
}

/// @brief 设置设备的波特率
/// @param serial_device  设备
/// @param baud 波特率
/// @return 成功 0 失败返回 -1
int app_serial_setBaud(SerialDevice * serial_device,Baud baud)
{
    struct termios options;
    //获取当前设备的配置
    if(tcgetattr(serial_device->super.fd,&options) != 0)
    {
        log_warn("app_serial_setBaud set fail");
        return -1;
    }
    cfsetispeed(&options,baud); //设在输入波特率
    cfsetospeed(&options,baud); //设在输出波特率
    //应用设置 TCSAFLUSH这个表示设置需要耍姓后生效
    if(tcsetattr(serial_device->super.fd,TCSAFLUSH,&options) < 0)
    {
        log_warn("app_serial_setBaud set fail");
        return -1;
    }
    return 1;
}


/// @brief 设置设备的数据位
/// @param serial_device 设备
/// @param data_dits 数据位数
/// @return 成功 0 失败返回 -1
int app_serial_setDataDits(SerialDevice * serial_device,DataDits data_dits)
{
    struct termios options;
    //获取当前设备的配置
    tcgetattr(serial_device->super.fd,&options);
    //清除CSIZE位
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= data_dits;  //设置数据位
    //应用设置 TCSAFLUSH这个表示设置需要耍姓后生效
    return tcsetattr(serial_device->super.fd,TCSAFLUSH,&options);
}


/// @brief 设置数据的停止位
/// @param serial_device  设备
/// @param syop_dits 停止位
/// @return 成功 0 失败返回 -1
int app_serial_setStopDits(SerialDevice * serial_device,StopDits syop_dits)
{
    struct termios options;
    //获取当前设备的配置
    tcgetattr(serial_device->super.fd,&options);
    //清除 CSTOPB
    options.c_cflag &= ~CSTOPB;
    options.c_cflag |= syop_dits;  //设置停止位
    //应用设置 TCSAFLUSH这个表示设置需要耍姓后生效
    return tcsetattr(serial_device->super.fd,TCSAFLUSH,&options);
}


/// @brief 设置设备的较言位
/// @param serial_device 设备
/// @param parity 校验位
/// @return 成功 0 失败返回 -1
int app_serial_setParity(SerialDevice * serial_device,Parity paritys)
{
    struct termios options;
    //获取当前设备的配置
    tcgetattr(serial_device->super.fd,&options);
    //清除 PARENB 校验是能位  PARODD 奇偶校验位
    options.c_cflag &= ~(PARENB | PARODD);
    options.c_cflag |= paritys;  //设置校验位
    //应用设置 TCSAFLUSH这个表示设置需要耍姓后生效
    return tcsetattr(serial_device->super.fd,TCSAFLUSH,&options);
}


/// @brief 设置在读串口设备的rx（就算32里面那个别人给你发送数据然后数据到那个buffer（这个buffer三系统定的不是我们定的那个缓冲区））的数据的模式
/// @param serial_device 设备
/// @param number 当位1是阻塞模式至少读到一个数据   位0  没有数据200ms返回
/// @return 成功 0 失败返回 -1
int app_serial_setBlockMote(SerialDevice * serial_device,int number)
{
    struct termios options;
    //获取当前设备的配置
    tcgetattr(serial_device->super.fd,&options);
    //判断是 需要什么摸索
    if(number)
    {
        //阻塞莫舍
        options.c_cc[VMIN] = 1;    // 读取至少一个字节
        options.c_cc[VTIME] = 0;   //没有超时时间可以一直在那里等待
    }
    else
    {
        //不阻塞模式  VTIME = 2；    VMIN = 1；  在200ms后即使没有读到数诀也会返回
        options.c_cc[VMIN] = 0;    // 可以直接返回
        options.c_cc[VTIME] = 2;   //至少在那里等待200ms
    }
    //应用设置 TCSAFLUSH这个表示设置需要耍姓后生效
    return tcsetattr(serial_device->super.fd,TCSAFLUSH,&options);
}

/// @brief 这个是把串口从另一个串口接受到数据 用纯二进制保存在他的buffer（这个buffer三系统定的不是我们定的那个缓冲区，
/// 不会经国任何处理
/// @param serial_device  设备
/// @return 成功 0 失败返回 -1
int app_serial_setRawMode(SerialDevice *serial_device)
{
    struct termios options;
    //获取当前设备的配置
    tcgetattr(serial_device->super.fd,&options);
    cfmakeraw(&options);    //把接受到的数据不做任何处理  
    //应用设置 TCSAFLUSH这个表示设置需要耍姓后生效
    return tcsetattr(serial_device->super.fd,TCSAFLUSH,&options);
}


/// @brief 在设在任何属性 比如波特率。。。 都需要刷新一下才会生效  init初始化这个函数不用 内部已经执行
/// @param device 
/// @return 成功 0 失败返回 -1
int app_serial_flush(SerialDevice *device)
{
    //TCIFLUSH: 清除输入缓存（系统创建的不是我们创建的缓存）。
    //TCOFLUSH: 清除输出缓存（系统创建的不是我们创建的缓存）。
    //TCIOFLUSH: 清除输入和输出缓存（系统创建的不是我们创建的缓存）。
    return tcflush(device->super.fd, TCIOFLUSH);  //刷性
}